﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data;
using AxeFrog.Data;

namespace AxeFrog.Security.Data.MSSQL
{
	[Export("MSSQL", typeof(IRoleRepository))]
	[Export("MSSQL", typeof(IRepository<Role, long>))]
	public class RoleRepository : IRoleRepository
	{
		private readonly ConnectionContextProvider _ccp;

		[ImportingConstructor]
		public RoleRepository(ConnectionContextProvider ccp)
		{
			_ccp = ccp;
		}

		public void SetUserRole(long userID, long role)
		{
			SetUserRole(userID, role, null);
		}

		public void SetUserRole(long userID, long role, DateTime? expiryDate)
		{
			using (var cc = _ccp.Connect())
				cc.CreateProcedure("SetUserRole")
					.AddParameter("@UserID", userID)
					.AddParameter("@RoleID", role)
					.AddParameter("@ExpiryDate", expiryDate, DbType.DateTime)
					.Execute()
					.Dispose();
		}

		public void RemoveUserRole(long userID, long role)
		{
			using (var cc = _ccp.Connect())
				cc.CreateCommand("DELETE FROM [RoleMembership] WHERE UserID = @UserID AND RoleID = @RoleID")
					.AddParameter("@UserID", userID)
					.AddParameter("@RoleID", role)
					.Execute()
					.Dispose();
		}

		public List<Role> GetAllRoles()
		{
			var list = new List<Role>();
			using (var cc = _ccp.Connect())
				return cc.CreateCommand("SELECT * FROM [Role] ORDER BY [Position]")
					.ExecuteReader()
					.ReadEach(reader => list.Add(reader.MapTo<Role>()))
					.Return(list);
		}

		public Dictionary<long, List<long>> GetByUserIDList(IList<long> idList)
		{
			var map = new Dictionary<long, List<long>>();
			if (idList.Count == 0)
				return map;

			using (var cc = _ccp.Connect())
				return cc.CreateCommand("SELECT UserID, RoleID FROM [RoleMembership] WHERE UserID IN (" + idList.Concat(",") + ")")
					.ExecuteReader()
					.ReadEach(reader => {
					                    	List<long> list;
					                    	long userid = (long)reader[0];
					                    	if (!map.TryGetValue(userid, out list))
					                    	{
					                    		list = new List<long>();
					                    		map.Add(userid, list);
					                    	}
					                    	list.Add(Convert.ToInt32(reader[1]));
					})
					.Return(map);
		}

		public long[] GetByUserID(long id)
		{
			List<long> list = new List<long>();
			using (var cc = _ccp.Connect())
				return cc.CreateCommand("SELECT RoleID FROM [RoleMembership] WHERE UserID = @UserID")
					.AddParameter("@UserID", id)
					.ExecuteReader()
					.ReadEach(reader => list.Add(reader.MapTo<int>()))
					.Return(list.ToArray());
		}

		public long[] GetByUserID(long id, out AccessLevel accessLevel)
		{
			Dictionary<long, bool> map = new Dictionary<long, bool>();
			using (var cc = _ccp.Connect())
			{
				var cmd = cc.CreateCommand(@"SELECT m.RoleID, r.IsAdminRole FROM [RoleMembership] m
											INNER JOIN [Role] r ON r.ID = m.RoleID
											WHERE UserID = @UserID");
				cmd.AddParameter("@UserID", id);
				var dr = cmd.ExecuteReader();
				dr.ReadEach(reader => map[Convert.ToInt32(reader[0])] = (bool)reader[1]);
				if(map.ContainsKey(UserRole.SystemAdministrator))
					accessLevel = AccessLevel.SuperUser;
				else if(map.Values.Contains(true))
					accessLevel = AccessLevel.Administrator;
				else
					accessLevel = AccessLevel.Authenticated;
				return dr.Return(map.Keys.ToArray());
			}
		}
	}
}